/**
 *******************************************************************************
 * @FileName  : ring_buf.h
 * @Author    : GaoQiu
 * @CreateDate: 2020-02-18
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#ifndef RING_BUF_H_
#define RING_BUF_H_

#include "defs_types.h"

#if 1

/*! ring buffer state */
enum RingBufState{
	RINGBUF_EMPTY,
	RINGBUF_FULL,
	RINGBUF_HALFFULL, /*!< half full is neither full nor empty */
};

/*! Ring buffer control block */
typedef struct{
	uint8_t   *pBuf;
	uint32_t   bufSize;
    /* use the msb of the {read,write}_index as mirror bit. You can see this as
     * if the buffer adds a virtual mirror and the pointers point either to the
     * normal or to the mirrored buffer. If the write_index has the same value
     * with the read_index, but in a different mirror, the buffer is full.
     * While if the write_index and the read_index are the same and within the
     * same mirror, the buffer is empty. The ASCII art of the ringbuffer is:
     *
     *          mirror = 0                    mirror = 1
     * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
     * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Full
     * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
     *  read_idx-^                   write_idx-^
     *
     * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
     * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Empty
     * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
     * read_idx-^ ^-write_idx
     *
     * The tradeoff is we could only use 32KiB of buffer for 16 bit of index.
     * But it should be enough for most of the cases.
     *
     * Ref: http://en.wikipedia.org/wiki/Circular_buffer#Mirroring */
	uint16_t   readMirror: 1;
	uint16_t   readIndex: 15;
	uint16_t   writeMirror:1;
	uint16_t   writeIndex:15;
	/* as we use msb of index as mirror bit, the size should be signed and
     * could only be positive. */
}RingBuf_t;

/**
 * @brief : Ring buffer initilaization.
 * @param : pRingBuf   Pointer point to ring buffer.
 * @param : pBuf       Pointer point to memory.
 * @param : size       the size of buffer.
 */
void RingBufInit(RingBuf_t *pRingBuf, uint8_t *pBuf, uint32_t size);

/**
 * @brief : Put data to Ring buffer.
 * @param : pRingBuf   Pointer point to ring buffer.
 * @param : pdata      Pointer point to data buffer.
 * @param : len        the length of data.
 * @return: return the length of writing data
 */
uint32_t RingBufPut(RingBuf_t *pRingBuf, uint8_t *pdata, uint32_t len);

/**
 * @brief : Get data from Ring buffer.
 * @param : pRingBuf   Pointer point to ring buffer.
 * @param : pRes       Pointer point to result data buffer.
 * @param : len        the length of data.
 * @return: return the length of reading data
 */
uint32_t RingBufGet(RingBuf_t *pRingBuf, uint8_t *pRes, uint32_t len);

uint32_t RingBufGetEx(RingBuf_t *pRingBuf, uint8_t *pRes, uint32_t len);

uint32_t RingBufMoveReadIndex(RingBuf_t *pRingBuf, uint32_t len);

/**
 * @brief : Get a data from Ring buffer.
 * @param : pRingBuf   Pointer point to ring buffer.
 * @param : ch         Pointer point to result data buffer.
 * @return: return the length of reading data
 */
uint32_t RingBufGetChar(RingBuf_t *pRingBuf, uint8_t *ch);

/**
 * @brief : the length of data in ring buffer.
 * @param : pRingBuf   Pointer point to ring buffer.
 * @return: return the length of data in ring buffer.
 */
uint32_t RingBufDataLen(RingBuf_t *pRingBuf);

/**
 * @brief : the size of free in ring buffer.
 * @param : pRingBuf   Pointer point to ring buffer.
 * @return: return the size of free in ring buffer.
 */
static inline uint32_t RingBufFreeSize(RingBuf_t *pRingBuf)
{
	return pRingBuf->bufSize - RingBufDataLen(pRingBuf);
}

#else
/*!   Ring Buffer error code. */
#define RING_BUF_SUCCESS      0x00
#define RING_BUF_ERR_FULL     0x01
#define RING_BUF_ERR_EMPTY    0x02

/*!   Ring buffer type */
typedef struct{
	uint8_t r;
	uint8_t w;
	uint8_t *pBuf;
	uint8_t num;
}ringBuf_t;


void    RB_Init(ringBuf_t *pRingBuf, uint8_t *pBuf, uint32_t num);
void    RB_Flush(ringBuf_t *pRingBuf);
bool_t  RB_IsEmpty(ringBuf_t *pRingBuf);
bool_t  RB_IsFull(ringBuf_t *pRingBuf);
bool_t  RB_GetDataLength(ringBuf_t *pRingBuf);
uint8_t RB_PutByte(ringBuf_t *pRingBuf, uint8_t data);
uint8_t RB_GetByte(ringBuf_t *pRingBuf, uint8_t *pData);
uint8_t RB_GetBytes(ringBuf_t *pRingBuf, uint8_t *pData, uint32_t len);
#endif

#endif /* RING_BUF_H_ */
